#pragma once
#include <iostream>
#include <queue>
#include <pthread.h>
#include <unistd.h>


class Task
{
public:
  Task(int a = 0, int b = 0)
    :_a(a)
    ,_b(b)
  {}
  int Run()
  {
    return _a + _b;
  }
  int GetA()
  {
    return _a;
  }
  int GetB()
  {
    return _b;
  }
private:
  int _a;
  int _b;
};

template<class T>
class BlockQueue
{
public:
  BlockQueue(int capacity = 5)
    :_capacity(capacity)
  {
    pthread_mutex_init(&_lock, nullptr);
    pthread_cond_init(&_c_cond, nullptr);
    pthread_cond_init(&_p_cond, nullptr);
  }
  void ProductData(T data)
  {
    LockQueue();
    while (IsFull()){
      // 让productor挂起等待
      ProductorWait();
    }
    // 放数据
    _q.push(data);
    // 唤醒consumer
    WakeUpConsumer();
    UnLockQueue();
  }
  void ConsumeData(T& data)
  {
    LockQueue();
    while (IsEmpty()){
      // 让consumer挂起等待
      ConsumerWait();
    }
    // 取数据
    data = _q.front();
    _q.pop();
    // 唤醒productor
    WakeUpProductor();
    UnLockQueue();
  }
  ~BlockQueue()
  {
    pthread_mutex_destroy(&_lock);
    pthread_cond_destroy(&_c_cond);
    pthread_cond_destroy(&_p_cond);
  }
private:
  bool IsFull()
  {
    return _q.size() == _capacity;
  }
  bool IsEmpty()
  {
    return _q.empty();
  }
  void ConsumerWait()
  {
    std::cout << "consumer wait...." << std::endl;
    pthread_cond_wait(&_c_cond, &_lock);
  }
  void WakeUpConsumer()
  {
    std::cout << "wake up consumer...." << std::endl;
    pthread_cond_broadcast(&_c_cond);
  }
  void ProductorWait()
  {
    std::cout << "productor wait...." << std::endl;
    pthread_cond_wait(&_p_cond, &_lock);
  }
  void WakeUpProductor()
  {
    std::cout << "wake up productor...." << std::endl;
    pthread_cond_broadcast(&_p_cond);
  }
  void LockQueue()
  {
    pthread_mutex_lock(&_lock);
  }
  void UnLockQueue()
  {
    pthread_mutex_unlock(&_lock);
  }
private:
  std::queue<T> _q;
  size_t         _capacity;// 队列最大容量
  pthread_mutex_t _lock;// 锁
  pthread_cond_t  _c_cond;// 消费者被唤醒和挂起的条件变量
  pthread_cond_t  _p_cond;// 生产者被唤醒和挂起的条件变量
};
